﻿using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2024 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shaco.Instance.FindReference
{
    public class FindAsmdefReferenceTool
    {
        [System.Serializable]
        public class JsonResult
        {
            public string name;
            public string rootNamespace;
            public List<string> references;
            public List<string> includePlatforms;
            public List<string> excludePlatforms;
            public bool allowUnsafeCode;
            public bool overrideReferences;
            public List<string> precompiledReferences;
            public bool autoReferenced;
            public List<string> defineConstraints;
            public List<string> versionDefines;
            public bool noEngineReferences;
        }

        [MenuItem("Assets/Find Asmdef References In Project", false, 21)]
        static void FindAssemblyDepenciesMenu()
        {
            var asmdef = Selection.objects.Where(v => v.GetType() == typeof(UnityEditorInternal.AssemblyDefinitionAsset)).Select(v => (UnityEditorInternal.AssemblyDefinitionAsset)v);
            FindAssemblyDepencies(asmdef, null);
        }

        [MenuItem("Assets/Find Asmdef References In Project", true, 21)]
        static bool FindAssemblyDepenciesValid()
        {
            return Selection.objects.Any(v => v.GetType() == typeof(UnityEditorInternal.AssemblyDefinitionAsset));
        }

        static public void FindAssemblyDepencies(UnityEditorInternal.AssemblyDefinitionAsset asmdef, IEnumerable<string> ignoreAssemblyGUID)
        {
            FindAssemblyDepencies(new UnityEditorInternal.AssemblyDefinitionAsset[] { asmdef }, ignoreAssemblyGUID);
        }

        static public IEnumerable<string> FindAssemblyDepencies(IEnumerable<UnityEditorInternal.AssemblyDefinitionAsset> asmdefs, IEnumerable<string> ignoreAssemblyGUID)
        {
            var retValue = new List<string>();
            var allDependGUID = new Dictionary<string, IEnumerable<string>>();
            foreach (var iter in asmdefs)
            {
                var parentGUID = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(iter));
                var depenciesGUID = FindAssemblyDepenciesDeep(parentGUID, ignoreAssemblyGUID);
                allDependGUID.Add(parentGUID, depenciesGUID);
            }

            foreach (var iter in allDependGUID)
            {
                var assetPath = AssetDatabase.GUIDToAssetPath(iter.Key);
                if (iter.Value.Count() > 0)
                {
                    Debug.Log($"<color=white>Select asmdef={assetPath}</color>", AssetDatabase.LoadAssetAtPath<Object>(assetPath));
                    Debug.Log("↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓");
                    foreach (var iter2 in iter.Value)
                    {
                        var subAssetPath = AssetDatabase.GUIDToAssetPath(iter2);
                        retValue.Add(subAssetPath);
                        Debug.Log("referenced by=" + subAssetPath, AssetDatabase.LoadAssetAtPath<Object>(subAssetPath));
                    }
                    Debug.Log("↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑");
                }
                else
                    Debug.Log($"no referenced asmdef={assetPath}", AssetDatabase.LoadAssetAtPath<Object>(assetPath));
            }
            return retValue;
        }

        static public IEnumerable<string> FindAssemblyDepenciesDeep(string parentGUID, IEnumerable<string> ignoreGUID)
        {
            Dictionary<string, bool> checkedGUID = new();
            var allAsmdefGUID = AssetDatabase.FindAssets("t:asmdef", new string[] { "Assets" }).ToDictionary(v => v, v => true);
            var ignoreGUIDArray = null == ignoreGUID ? new string[0] : ignoreGUID.ToArray();
            return FindAssemblyDepenciesDeep(parentGUID, ref allAsmdefGUID, ref checkedGUID, ref ignoreGUIDArray);
        }

        static public string ParseGUIDString(string guid)
        {
            return guid.Substring(guid.IndexOf("GUID:") + "GUID:".Length);
        }

        static IEnumerable<string> FindAssemblyDepenciesDeep(string parentGUID, ref Dictionary<string, bool> allAsmdefGUID, ref Dictionary<string, bool> checkedPath, ref string[] ignoreGUID)
        {
            List<string> retValue = new();
            var parentAssetPath = AssetDatabase.GUIDToAssetPath(parentGUID);
            if (checkedPath.ContainsKey(parentAssetPath))
            {
                // var errorPath = AssetDatabase.GUIDToAssetPath(parentGUID);
                // Debug.LogError("循环引用=" + errorPath, AssetDatabase.LoadAssetAtPath<Object>(errorPath));
                return retValue;
            }
            checkedPath.Add(parentAssetPath, true);

            foreach (var iter in allAsmdefGUID)
            {
                var guid = iter.Key;
                var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                var loadAsmdef = AssetDatabase.LoadAssetAtPath<UnityEditorInternal.AssemblyDefinitionAsset>(assetPath);
                var jsonRoot = JsonUtility.FromJson<JsonResult>(loadAsmdef.text);
                var references = jsonRoot.references;
                foreach (var iter2 in references)
                {
                    var subGUID = ParseGUIDString(iter2);
                    if (subGUID == parentGUID)
                    {
                        if (ignoreGUID.Contains(guid))
                            continue;

                        if (!checkedPath.ContainsKey(AssetDatabase.GUIDToAssetPath(guid)))
                            retValue.Add(guid);
                        retValue.AddRange(FindAssemblyDepenciesDeep(guid, ref allAsmdefGUID, ref checkedPath, ref ignoreGUID));
                        break;
                    }
                }
            }
            return retValue;
        }
    }
}